package com.ruoyi.web.controller;

import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * SSE实时推送控制器
 *
 * @author ruoyi
 */
@Slf4j
@RestController
@RequestMapping("/sse")
public class SseController {

    // 存储用户连接
    private static final ConcurrentHashMap<Long, SseEmitter> USER_CONNECTIONS = new ConcurrentHashMap<>();

    // 存储所有连接（用于广播）
    private static final CopyOnWriteArraySet<SseEmitter> ALL_CONNECTIONS = new CopyOnWriteArraySet<>();

    /**
     * 从token获取用户ID
     */
    private Long getUserIdFromToken(String token) {
        // 如果URL参数中有token，先进行验证
        if (token != null && !token.isEmpty()) {
            try {
                Object loginId = StpUtil.getLoginIdByToken(token);
                if (loginId == null) {
                    log.error("SSE连接失败: 无效的token");
                    throw new RuntimeException("无效的token");
                }

                // 处理Sa-Token的loginId格式 (可能是 "sys_user:5" 格式)
                String loginIdStr = loginId.toString();
                Long userId;
                if (loginIdStr.contains(":")) {
                    // 提取冒号后面的数字部分
                    String[] parts = loginIdStr.split(":");
                    userId = Long.valueOf(parts[parts.length - 1]);
                } else {
                    userId = Long.valueOf(loginIdStr);
                }

                log.info("SSE连接通过token验证成功，loginId: {}, 用户ID: {}", loginIdStr, userId);
                return userId;
            } catch (Exception e) {
                log.error("SSE连接token验证失败: {}", e.getMessage());
                throw new RuntimeException("认证失败: " + e.getMessage());
            }
        } else {
            // 如果没有token参数，尝试从当前会话获取
            try {
                Long userId = StpUtil.getLoginIdAsLong();
                log.info("SSE连接通过会话验证成功，用户ID: {}", userId);
                return userId;
            } catch (Exception e) {
                log.error("SSE连接会话验证失败: {}", e.getMessage());
                throw new RuntimeException("未登录或会话已过期");
            }
        }
    }

    /**
     * 建立SSE连接
     */
    @GetMapping(value = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter connect(@RequestParam(required = false) String token) {
        final Long userId = getUserIdFromToken(token);

        log.info("开始建立SSE连接，用户ID: {}", userId);
        log.info("用户 {} 建立SSE连接", userId);

        // 创建SSE连接，设置超时时间为30分钟
        SseEmitter emitter = new SseEmitter(30 * 60 * 1000L);

        // 存储连接
        USER_CONNECTIONS.put(userId, emitter);
        ALL_CONNECTIONS.add(emitter);

        // 设置连接完成回调
        emitter.onCompletion(() -> {
            log.info("用户 {} SSE连接完成", userId);
            USER_CONNECTIONS.remove(userId);
            ALL_CONNECTIONS.remove(emitter);
        });

        // 设置连接超时回调
        emitter.onTimeout(() -> {
            log.info("用户 {} SSE连接超时", userId);
            USER_CONNECTIONS.remove(userId);
            ALL_CONNECTIONS.remove(emitter);
        });

        // 设置连接错误回调
        emitter.onError((throwable) -> {
            log.error("用户 {} SSE连接错误", userId, throwable);
            USER_CONNECTIONS.remove(userId);
            ALL_CONNECTIONS.remove(emitter);
        });

        try {
            // 发送连接成功消息
            emitter.send(SseEmitter.event()
                    .name("connect")
                    .data("连接成功")
                    .id(String.valueOf(System.currentTimeMillis())));
        } catch (IOException e) {
            log.error("发送连接成功消息失败", e);
        }

        return emitter;
    }

    /**
     * 向指定用户发送消息
     */
    @PostMapping("/send/{userId}")
    public String sendToUser(@PathVariable Long userId, @RequestBody String message) {
        SseEmitter emitter = USER_CONNECTIONS.get(userId);
        if (emitter != null) {
            try {
                emitter.send(SseEmitter.event()
                        .name("message")
                        .data(message)
                        .id(String.valueOf(System.currentTimeMillis())));
                log.info("向用户 {} 发送消息成功: {}", userId, message);
                return "发送成功";
            } catch (IOException e) {
                log.error("向用户 {} 发送消息失败", userId, e);
                // 移除失效连接
                USER_CONNECTIONS.remove(userId);
                ALL_CONNECTIONS.remove(emitter);
                return "发送失败：连接已断开";
            }
        } else {
            log.warn("用户 {} 未建立SSE连接", userId);
            return "发送失败：用户未连接";
        }
    }

    /**
     * 向指定用户发送排班通知 (内部调用，跳过认证)
     */
    @PostMapping("/sendScheduleNotification/{userId}")
    public String sendScheduleNotification(@PathVariable Long userId, @RequestBody Map<String, Object> notification) {
        // 内部调用，直接推送，不需要认证
        return sendNotificationInternal(userId, notification);
    }

    /**
     * 内部推送方法
     */
    private String sendNotificationInternal(Long userId, Map<String, Object> notification) {
        SseEmitter emitter = USER_CONNECTIONS.get(userId);
        if (emitter != null) {
            try {
                emitter.send(SseEmitter.event()
                        .name("scheduleNotification")
                        .data(notification)
                        .id(String.valueOf(System.currentTimeMillis())));
                log.info("向用户 {} 发送排班通知成功: {}", userId, notification.get("title"));
                return "发送成功";
            } catch (IOException e) {
                log.error("向用户 {} 发送排班通知失败", userId, e);
                // 移除失效连接
                USER_CONNECTIONS.remove(userId);
                ALL_CONNECTIONS.remove(emitter);
                return "发送失败：连接已断开";
            }
        } else {
            log.warn("用户 {} 未建立SSE连接", userId);
            return "发送失败：用户未连接";
        }
    }

    /**
     * 广播消息给所有连接的用户
     */
    @PostMapping("/broadcast")
    public String broadcast(@RequestBody String message) {
        int successCount = 0;
        int failCount = 0;

        for (SseEmitter emitter : ALL_CONNECTIONS) {
            try {
                emitter.send(SseEmitter.event()
                        .name("broadcast")
                        .data(message)
                        .id(String.valueOf(System.currentTimeMillis())));
                successCount++;
            } catch (IOException e) {
                log.error("广播消息失败", e);
                ALL_CONNECTIONS.remove(emitter);
                failCount++;
            }
        }

        log.info("广播消息完成，成功: {}, 失败: {}", successCount, failCount);
        return String.format("广播完成，成功: %d, 失败: %d", successCount, failCount);
    }

    /**
     * 获取当前连接数
     */
    @GetMapping("/connections")
    public String getConnections() {
        return String.format("当前连接数: %d", USER_CONNECTIONS.size());
    }

    /**
     * 排班通知DTO
     */
    public static class ScheduleNotificationDto {
        private String title;
        private String content;
        private String messageType;
        private String scheduleDate;
        private String shiftName;

        // Getters and Setters
        public String getTitle() { return title; }
        public void setTitle(String title) { this.title = title; }

        public String getContent() { return content; }
        public void setContent(String content) { this.content = content; }

        public String getMessageType() { return messageType; }
        public void setMessageType(String messageType) { this.messageType = messageType; }

        public String getScheduleDate() { return scheduleDate; }
        public void setScheduleDate(String scheduleDate) { this.scheduleDate = scheduleDate; }

        public String getShiftName() { return shiftName; }
        public void setShiftName(String shiftName) { this.shiftName = shiftName; }
    }
}
